package redis

import (
	"context"
	"errors"
	"fmt"
	"time"

	goRedis "github.com/redis/go-redis/v9"
)

const DefaultTimeOut time.Duration = time.Duration(5) * time.Second

var ErrRedisKeyNil = errors.New("redis key is nil or empty")

func IntToTimeDuration(t int32) time.Duration {
	if t > 0 {
		return time.Duration(t) * time.Second
	} else if t == 0 {
		return 0
	} else {
		return -1
	}
}

func InitRedis(c *RedisConf) (*goRedis.Client, error) {
	if c == nil {
		return nil, errors.New("redis configuration cannot be nil")
	}
	ops := goRedis.Options{
		Addr:     fmt.Sprintf("%s:%d", c.Host, c.Port),
		Password: c.Password,
		DB:       int(c.Db),
	}
	client := goRedis.NewClient(&ops)
	ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeOut)
	defer cancel()
	err := client.Ping(ctx).Err()
	if err != nil {
		client.Close()
		return nil, err
	}
	return client, nil
}

func CloseRedis(client *goRedis.Client) error {
	if client != nil {
		return client.Close()
	}
	return nil
}

func RedisExists(
	client *goRedis.Client,
	timeout time.Duration,
	keys ...string,
) (int64, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.Exists(ctx, keys...).Result()
}

func RedisGet(
	client *goRedis.Client,
	timeout time.Duration,
	key string,
) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.Get(ctx, key).Result()
}

func RedisMGet(
	client *goRedis.Client,
	timeout time.Duration,
	keys ...string,
) ([]any, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.MGet(ctx, keys...).Result()
}

func RedisSet(
	client *goRedis.Client,
	timeout time.Duration,
	key string,
	value any,
	expiration time.Duration,
) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.Set(ctx, key, value, expiration).Err()
}

func RedisMSet(
	client *goRedis.Client,
	timeout time.Duration,
	values ...any,
) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.MSet(ctx, values...).Err()
}

func RedisHSet(
	client *goRedis.Client,
	timeout time.Duration,
	key string,
	values ...any,
) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.HSet(ctx, key, values...).Err()
}

func RedisDel(
	client *goRedis.Client,
	timeout time.Duration,
	keys ...string,
) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.Del(ctx, keys...).Err()
}

func RedisSAdd(
	client *goRedis.Client,
	timeout time.Duration,
	key string,
	values ...any,
) error {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.SAdd(ctx, key, values...).Err()
}

func RedisInSet(
	client *goRedis.Client,
	timeout time.Duration,
	key string,
	value any,
) (bool, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	return client.SIsMember(ctx, key, value).Result()
}
